#include <algorithm>
#include "SQLSelectGroupByClause.h"

#include "../expr/SQLBinaryOpExpr.h"
#include "../expr/SQLIntegerExpr.h"
#include "../../../utils/instanceof.h"
#include "../../visitor/SQLASTVisitor.h"

SQLSelectGroupByClause::SQLSelectGroupByClause()
{
  items = std::make_shared<std::list<SQLExpr_ptr>>();
}

void SQLSelectGroupByClause::accept0(SQLASTVisitor_ptr visitor)
{
  if (visitor->visit(SharedObject(SQLSelectGroupByClause)))
  {
    for (int i = 0; i < this->items->size(); i++)
    {
      std::list<SQLExpr_ptr>::iterator it = items->begin();
      advance(it, i);
      if (it != items->end())
      {
        (*it)->accept(visitor);
      }
    }

    if (having != NULL)
    {
      having->accept(visitor);
    }
  }

  visitor->endVisit(SharedObject(SQLSelectGroupByClause));
}

BOOL_ptr SQLSelectGroupByClause::isDistinct()
{
  return distinct;
}

void SQLSelectGroupByClause::setDistinct(BOOL_ptr distinct)
{
  this->distinct = distinct;
}

BOOL_ptr SQLSelectGroupByClause::isWithRollUp()
{
  return withRollUp;
}

void SQLSelectGroupByClause::setWithRollUp(BOOL_ptr withRollUp)
{
  this->withRollUp = withRollUp;
}

BOOL_ptr SQLSelectGroupByClause::isWithCube()
{
  return withCube;
}

void SQLSelectGroupByClause::setWithCube(BOOL_ptr withCube)
{
  this->withCube = withCube;
}

SQLExpr_ptr SQLSelectGroupByClause::getHaving()
{
  return this->having;
}

void SQLSelectGroupByClause::setHaving(SQLExpr_ptr having)
{
  if (having != NULL)
  {
    having->setParent(SharedObject(SQLSelectGroupByClause));
  }

  this->having = having;
}

void SQLSelectGroupByClause::addHaving(SQLExpr_ptr condition)
{
  if (condition == NULL)
  {
    return;
  }

  if (having == NULL)
  {
    having = condition;
  }
  else
  {
    having = SQLBinaryOpExpr::and_(having, condition);
  }
}

SQLExpr_list_ptr  SQLSelectGroupByClause::getItems()
{
  return this->items;
}

BOOL_ptr SQLSelectGroupByClause::containsItem(SQLExpr_ptr item)
{
  // return this->items.contains(item);
  auto it = std::find(this->items->begin(), this->items->end(), item);
  if( it != this->items->end()){
    return BOOL::TRUE;
  }else{
    return BOOL::FALSE;
  }
}

void SQLSelectGroupByClause::addItem(SQLExpr_ptr sqlExpr)
{
  if (sqlExpr != NULL)
  {
    sqlExpr->setParent(SharedObject(SQLSelectGroupByClause));
    this->items->push_back(sqlExpr);
  }
}

void SQLSelectGroupByClause::addItem(int index, SQLExpr_ptr sqlExpr)
{
  if (sqlExpr != NULL)
  {
    sqlExpr->setParent(SharedObject(SQLSelectGroupByClause));
    std::list<SQLExpr_ptr>::iterator it = items->begin();
    std::advance(it, index);
    this->items->insert(it, 1, sqlExpr);
  }
}

SQLObject_ptr SQLSelectGroupByClause::clone()
{
  SQLObject_ptr clone_tmp = std::shared_ptr<SQLSelectGroupByClause>(new SQLSelectGroupByClause());
  std::shared_ptr<SQLSelectGroupByClause> x = std::dynamic_pointer_cast<SQLSelectGroupByClause>(clone_tmp);
  // SQLSelectGroupByClause *x = new SQLSelectGroupByClause();
  for (SQLExpr_ptr item : *items)
  {
    SQLExpr_ptr item2 = std::dynamic_pointer_cast<SQLExpr>(item->clone());
    item2->setParent(x);
    x->items->push_back(item2);
  }
  if (having != NULL)
  {
    x->setHaving(std::dynamic_pointer_cast<SQLExpr>(having->clone()));
  }
  x->withRollUp = withRollUp;
  x->withCube = withCube;
  x->distinct = distinct;
  x->paren = paren;
  if (hint != NULL)
  {
    x->setHint(std::dynamic_pointer_cast<SQLCommentHint>(hint->clone()).get());
  }
  return x;
}

BOOL_ptr SQLSelectGroupByClause::replace(SQLExpr_ptr expr, SQLExpr_ptr target)
{
  if (expr == having)
  {
    setHaving(target);
    return BOOL::TRUE;
  }

  for (int i = items->size() - 1; i >= 0; i--)
  {
    std::list<SQLExpr_ptr>::iterator it = items->begin();
    std::advance(it, i); // 迭代器前进i个元素，注意i是从0开始
    if ((*it) == expr)
    {
      if (instanceof <SQLExpr, SQLIntegerExpr>(target))
      {
        if (it != items->end()) // 最后一个不能删除
          items->erase(it);     // 删除第i个元素
      }
      else
      {
        (*it) == target;
      }
      return BOOL::TRUE;
    }
  }
  return BOOL::FALSE;
}

SQLCommentHint *SQLSelectGroupByClause::getHint()
{
  return hint;
}

void SQLSelectGroupByClause::setHint(SQLCommentHint *hint)
{
  this->hint = hint;
}

BOOL_ptr SQLSelectGroupByClause::isParen()
{
  return paren;
}

void SQLSelectGroupByClause::setParen(BOOL_ptr paren)
{
  this->paren = paren;
}

bool SQLSelectGroupByClause::equals(Object_ptr o)
{
  if (this == o.get())
  {
    return true;
  }
  if (o == NULL || getClass() != o->getClass())
  {
    return false;
  }

  std::shared_ptr<SQLSelectGroupByClause> that = std::dynamic_pointer_cast<SQLSelectGroupByClause>(o);

  if (withRollUp != that->withRollUp)
  {
    return false;
  }
  if (withCube != that->withCube)
  {
    return false;
  }
  if (distinct != that->distinct)
  {
    return false;
  }
  if (paren != that->paren)
  {
    return false;
  }
  if (!items->empty() ? !(items == that->items) : !(that->items->empty()))
  {
    return false;
  }
  if (having != NULL ? !(having == that->having) : that->having != NULL)
  {
    return false;
  }
  return hint != NULL ? (hint == that->hint) : that->hint == NULL;
}

int SQLSelectGroupByClause::hashCode()
{
  // int result = items != NULL ? items->hashCode() : 0;
  int result = 0;
  result = 31 * result + (having != NULL ? having->hashCode() : 0);
  result = 31 * result + (withRollUp ? 1 : 0);
  result = 31 * result + (withCube ? 1 : 0);
  result = 31 * result + (distinct ? 1 : 0);
  result = 31 * result + (paren ? 1 : 0);
  result = 31 * result + (hint != NULL ? hint->hashCode() : 0);
  return result;
}
